#include "BaseCode.h"
#include "MyString.h"
#include "MyIni.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CMyIni::CMyIni(const char* pszFile)
{
	bool bSucOpen = this->Open(pszFile);
	if (!bSucOpen)
		BC_LOGERROR("%s open error.", pszFile);
	
	m_strFileName = pszFile;
}

CMyIni::~CMyIni()
{

}

//////////////////////////////////////////////////////////////////////
bool CMyIni::IsValidLine	(const char* szLine) const
{
	IF_NOT (szLine)
		return false;

	// length check
	if (::strlen(szLine) <= 2)
		return false;

	// first char check
	bool bValidLine = true;
	switch(szLine[0])
	{
	case '/':
	case '\\':
	case ';':
	case '=':
	case ' ':
	case '\t':
	case '\r':
	case 0x0a:
		bValidLine = false;
		break;

	default:
		break;
	}

	return bValidLine;
}

//////////////////////////////////////////////////////////////////////
bool CMyIni::ParseSection(char* szLine, CMyString& str) const
{
	IF_NOT (szLine)
		return false;

	if ('[' != szLine[0])
		return false;

	int nStrLen = ::strlen(szLine);
	int i;
	for (i=1; i<nStrLen; i++)
	{
		if (']' == szLine[i])
		{
			szLine[i] = '\0';
			break;
		}
	}
	
	if (i >= nStrLen)	// not valid section line
		return false;
		
	// section line found!
	str = szLine+1;

	return true;		
}

//////////////////////////////////////////////////////////////////////
bool CMyIni::ParseContent(char* szLine, CMyString& strIndex, CMyString& strContent) const
{
	// valid check
	if (!this->IsValidLine(szLine))
		return false;

	// parse index
	int nLen = ::strlen(szLine);
	int i;
	for (i=0; i<nLen; i++)
	{
		if ('=' == szLine[i])
		{
			szLine[i] = '\0';

			int idx = i;
			while (idx > 0)
			{
				--idx;

				int c = szLine[idx];
				if (' ' != c && '\t' != c)
					break;
				else
					szLine[idx] = '\0';
			}

			strIndex = szLine;
			break;
		}
	}

	if (i >= nLen)		// no '=' found, not valid line
		return false;

	// string forward
	if (i+1 >= nLen)	// empty content
		return true;

	szLine	+= (i+1);	// now szLine is string behind '='

	// remove format char
	nLen = ::strlen(szLine);
	for (i=0; i<nLen; i++)
	{
		int c = szLine[i];
		if (' ' != c && '\t' != c)
			break;
	}

	szLine += i;		// now szLine is string after format char
	
	// parse content
	nLen = ::strlen(szLine);
	for (i=0; i<nLen; i++)
	{
		if (/*' ' == szLine[i] || */'\t' == szLine[i] || ';' == szLine[i] 
				|| '\r' == szLine[i] || 0x0a == szLine[i])
		{
			szLine[i] = '\0';
			break;
		}
	}
	// trim right
	nLen = ::strlen(szLine);
	for (i=nLen-1; i>=0; i--)
	{
		if (' ' != szLine[i])
			break;
	}
	szLine[i+1] = '\0';

	strContent = szLine;
	return true;
}

//////////////////////////////////////////////////////////////////////
bool CMyIni::Open (const char* pszIniFile)
{
	IF_NOT (pszIniFile)
		return false;

	m_setSection.clear();

	FILE* fp = NULL;
	if (0 != fopen_s(&fp, pszIniFile, "r"))
		return false;

	SECTION section;
	CMyString strTitle;
	
	char szLine[1024]	= "";
	while(true)
	{
		if (NULL == fgets(szLine, sizeof(szLine), fp))
		{
			// save section info
			if (!strTitle.empty())
				m_setSection[strTitle] = section;

			break;
		}

		// string length chk
		int nStrLen = ::strlen(szLine);
		if (nStrLen <= 2)
			continue;

		// get rid of end char
		if (0x0a == szLine[nStrLen-1])
			szLine[nStrLen-1] = 0;

		// parse now
		CMyString str;
		if (this->ParseSection(szLine, str))
		{
			// a new section found, keep the old one
			if (!strTitle.empty())
				m_setSection[strTitle] = section;

			// replace section title string
			strTitle = str;
			
			// clear old section
			section.setInfo.clear();
		}
		else
		{
			if (!strTitle.empty())
			{
				// read section content
				CMyString strIndex, strContent;
				if (this->ParseContent(szLine, strIndex, strContent))
				{
					section.setInfo[strIndex] = strContent;
				}
			}
		}
	}

	fclose(fp);

	m_strFileName = pszIniFile;
	return true;
}

//////////////////////////////////////////////////////////////////////
//template<typename T>
//T	CMyIni::GetData(const char* pszSection, const char* pszIndex) const
//{
//	return GetStr(pszSection, pszIndex);
//}

//////////////////////////////////////////////////////////////////////
int CMyIni::GetData(const char* pszSection, const char* pszIndex) const
{
	return GetStr(pszSection, pszIndex);
}

//////////////////////////////////////////////////////////////////////
const char* CMyIni::GetString(const char* pszSection, const char* pszIndex) const
{
	return GetStr(pszSection, pszIndex);
}

//////////////////////////////////////////////////////////////////////
float CMyIni::GetFloat(const char* pszSection, const char* pszIndex) const
{
	return GetStr(pszSection, pszIndex);
}

//////////////////////////////////////////////////////////////////////
const CMyString& CMyIni::GetStr(const char* pszSection, const char* pszIndex) const
{
	IF_NOT (pszSection && pszIndex)
		return m_strTmp;
	
	// search section
	std::map<CMyString, SECTION>::const_iterator iter = m_setSection.find(pszSection);
	if (iter == m_setSection.end())
	{
// 		if (m_bCritical)
// 			BC_LOGERROR("section[%s] not found in %s!", pszSection, m_strFileName.c_str());
		
		return m_strTmp;
	}
	
	// search content
	std::map<CMyString, CMyString>::const_iterator iter2 = (*iter).second.setInfo.find(pszIndex);
	if (iter2 == (*iter).second.setInfo.end())
	{
// 		if (m_bCritical)
// 			BC_LOGERROR("section[%s], index[%s] not found in %s!", pszSection, pszIndex, m_strFileName.c_str());
		
		return m_strTmp;
	}
	
	return (*iter2).second;
}

//////////////////////////////////////////////////////////////////////
// static
//////////////////////////////////////////////////////////////////////
CMyIni*	CMyIni::CreateNew(const char* pszIniFile)
{
	CHECKF (pszIniFile);

	CMyIni* pMyIni = new CMyIni(pszIniFile);
	IF_NOT (pMyIni)
		return NULL;

	if (!pMyIni->Open(pszIniFile))
	{
		delete pMyIni;
		return NULL;
	}

	return pMyIni;
}
